package gen.parser;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;

public class Parser {
	
	private HashMap<String, String> charClasses = new HashMap<String, String>();
	private HashMap<String, String> identifiers = new HashMap<String, String>();
	
	final String SPECIAL_CHARS = "*+|[]()";

	public Parser(String filePath) {
		
		try {
			FileInputStream fStream = new FileInputStream(filePath);
			BufferedReader buffReader = new BufferedReader(new InputStreamReader(fStream));
			
			boolean storingCharClass = false, storingIdentifiers = false;
			//Parse document line by line
			String currLine;
			while ((currLine = buffReader.readLine()) != null) {
				//System.out.println(currLine);
				
				if (currLine.length() > 1) {
					if (currLine.substring(0, 2).equals("%%")) {
						if (!storingCharClass)
							storingCharClass = true;
						else {
							storingCharClass = false;
							storingIdentifiers = true;
						}
					
						continue;
					}
					else if (storingCharClass)
						getCharClass(currLine);
					else if (storingIdentifiers)
						getIdentifier(currLine);
				}
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Parse document line for character class
	 * @param docLine - Line to parse
	 */
	private void getCharClass(String docLine) {
		
		String charClassLabel, charClassRegex;
		charClassLabel = getRegexLabel(docLine);
		if (charClassLabel != null) {
			charClassRegex = getCharRegex(docLine.substring(charClassLabel.length()));
			if (charClassRegex != null && RegexValidator.validate(charClassRegex))
				charClasses.put(charClassLabel, charClassRegex);
		}
		
	}

	/**
	 * Parse line for Regex label
	 * @param currLine - String to parse
	 * @return Label of the regex
	 */
	private String getRegexLabel(String line) {
		if (line == null || line.length() <= 1)
			return null;
		else {
			StringBuilder label = new StringBuilder();
			boolean formingLabel = false;
			for (int i = 0; i < line.length(); i++) {
				char c = line.charAt(i);
				
				//Start forming label when a $ character is reached
				if (c == '$') {
					label.append(c);
					formingLabel = true;
				}
				//Stop forming label when space is reached
				else if (Character.isWhitespace(c)) {
					if (formingLabel)
						break;
					else 
						continue;
				}
				else
					label.append(c);
			}
			
			if (label.length() < 2)
				return null;
			else
				return label.toString();
		}
	}

	private String getCharRegex(String line) {
		if (line == null || line.length() <= 2)
			return null;
		else {
			StringBuilder regex = new StringBuilder();
			boolean formingRegex = false;
			
			for (int i = 0; i < line.length(); i++) {
				char c = line.charAt(i);
				
				//Start forming RegEx when a [ character is found
				if (c == '[') {
					regex.append(c);
					formingRegex = true;
				}
				//Stop forming RegEx when ] is reached
				else if (c == ']') {
					if (formingRegex) {
						regex.append(c);
						break;
					}
					else 
						continue;
				}
				else if (c != ' ' && formingRegex)
					regex.append(c);
				else if (c == ' ' && formingRegex)
					if (i > 0 && line.charAt(i-1) == '\\')
						regex.append(c);//line.substring(i-1, i+1));
				
			}
			
			//Check if we have an exception (ie: [^6] IN $DIGIT)
			String[] regexArr = line.split("IN");
			if (regexArr.length > 1) {
				regex.append(" IN ");
				
				String label = regexArr[1];
				String labelRegex = charClasses.get(getRegexLabel(label));
				
				if (labelRegex == null)
					return null;
				regex.append(labelRegex);
			}	
			
			return regex.toString();
		}
	}
	
	private void getIdentifier(String docLine) {
		String regexLabel, regex;
		regexLabel = getRegexLabel(docLine);
		if (regexLabel != null) {
			regex = getIdentifierRegex(docLine.substring(regexLabel.length()));
			if (regex != null)
				identifiers.put(regexLabel, regex);
		}
	}
	
	private String getIdentifierRegex(String line) {
		if (line == null || line.length() <= 1)
			return null;
		else {
			StringBuilder regex = new StringBuilder();
			StringBuilder label = new StringBuilder();
			boolean formingLabel = false;
			
			for (int i = 0; i < line.length(); i++) {
				char c = line.charAt(i);
				
				if (formingLabel) {
					//Continue forming label until a special character is reached
					if (Character.isWhitespace(c) || SPECIAL_CHARS.contains(""+c) || i == line.length()-1) {
						//Check if end of line reached
						if (i == line.length()-1)
							label.append(c);
						else
							i--;
						
						//Get RegEx and append to identifier RegEx
						String labelRegex = charClasses.get(label.toString());
						if (labelRegex != null)
							regex.append(labelRegex);
						label = new StringBuilder();
						
						formingLabel = false;
					}
					else
						label.append(c);
				}
				else {
					if (c == '$') {
						label.append(c);
						formingLabel = true;
						continue;
					}
					else if (!Character.isWhitespace(c))
						regex.append(c);
				}
			}
		
			//System.out.println(regex.toString());
			return regex.toString();
		}
	}

	@Override
	//Print out parse tree
	public String toString() {
		return charClasses.toString() + "\n" + identifiers.toString();
	}
}
